package com.steerinfo.dil.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.steerinfo.dil.annotaion.LogAround;
import com.steerinfo.dil.mapper.LogResultMapper;
import com.steerinfo.dil.model.LogResult;
import com.steerinfo.dil.util.DataChange;
import com.steerinfo.framework.controller.RESTfulResult;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 日志 Aspect切面,编写切入点的方法
 */
@Aspect
@Component
public class LogAspect {
    @Value("${prefix.logPrefix}")
    private String logPrefix;
    @Autowired
    LogResultMapper logResultMapper;

    static final Logger log = Logger.getLogger(LogAspect.class);

    private DateFormat format =new SimpleDateFormat("yyyy-MM-dd");


    /**
     * 定义切入点，所有该注解为切入点
     */
    @Pointcut("@annotation(com.steerinfo.dil.annotaion.LogAround)")
    public void AroundPointCut(){}

    /**
     * 环绕通知 @Around，日志文件同时记录方法的入参出参，数据库只保留成功的出参
     * @param point
     * @return
     *
     * @throws Throwable
     */
    @Around("AroundPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //获取该切点处的方法及入参
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        Object[] requestParams = point.getArgs();
        JSONObject map = null ;
        for(Object param:requestParams){
            if(param!=null && param instanceof Map){
                map = JSONObject.parseObject(JSONObject.toJSONString(param));
                break;
            }
        }
        if(map==null || map.get("userId")==null ||map.get("userName") ==null){
            log.error("缺乏日志必要参数"+map);
            return new RESTfulResult("500","缺乏日志必要参数"+map,"缺乏日志必要参数"+map);
        }
        //获取切点处的注解及其参数
        LogAround logAround = method.getAnnotation(LogAround.class);
        String[] foreignKeys = logAround.foreignKeys();
        String[] foreignKeyTypes = logAround.foreignKeyTypes();
        String description = logAround.description();
        //获取方法签名
        Signature signature = point.getSignature();
        String methodName = method.getName();
        log.info("执行===" + methodName + "===开始");
        log.info("方法描述：" + description);
        log.info("方法名：" + signature);
        log.info("方法参数：" + map);
        //记录当前时间
        Date beginTime = new Date();
        //执行方法，并取得返回值
        Object response = null;
        try{
            response = point.proceed();
        }catch (Exception e){
            //记录错误日志
            log.error("执行===" + methodName + "===异常："+ e.getClass().getName());
            log.error("方法描述：" + description);
            log.error("方法名：" + signature);
            log.error("方法参数：" + requestParams);
            throw e;
        }
        //解析返回值，记录日志
        try{
            RESTfulResult result = JSONObject.parseObject(JSONObject.toJSONString(response),RESTfulResult.class);
            //获取执行时间
            long exeCost = System.currentTimeMillis() - beginTime.getTime();
            if(RESTfulResult.SUCCEED.equals(result.getStatus())){
                //执行成功，记录日志
                Map<String,Object> data =(Map<String,Object>) result.getData();
                //记录正常日志
                log.info("方法返回：" + JSON.toJSONString(result));
                log.info("执行时间（ms）："+exeCost);
                log.info("执行===" + methodName + "===成功");
                //记录到数据库
                List<LogResult> logResults=new ArrayList<>();
                for(int i=0 ; i<foreignKeys.length ; i++){
                    LogResult logResult=new LogResult();
                    logResult.setLogId(logResultMapper.nextId());
                    logResult.setForeignKeyId(DataChange.dataToBigDecimal(data.get(foreignKeys[i])));
                    logResult.setForeignKeyType(foreignKeyTypes[i]);
                    logResult.setLogContent(map.get("userName")+":"+result.getMessage());
                    logResult.setMethodName(""+signature);
                    logResult.setMethodDescription(description);
                    logResult.setInsertTime(beginTime);
                    logResult.setExeCost(DataChange.dataToBigDecimal(exeCost));
                    logResult.setFilePath(logPrefix+format.format(beginTime));
                    logResult.setInsertUsername(""+map.get("userId"));
                    logResult.setRemark("自动记录");
                    logResults.add(logResult);
                }
                logResultMapper.batchInsert(logResults);
            }else{
                //记录失败日志
                log.info("方法返回：" + JSON.toJSONString(result));
                log.info("执行时间（ms）："+exeCost);
                log.info("执行===" + methodName + "===失败");
            }
        }catch (Exception e){
            //记录错误日志
            log.error("===日志解析、记录失败===：\n"+ e.getMessage());
        }
        return response;
    }
}
